Overview
Tavern is a teamserver for Realm, providing a UI to control deployments and implants during an engagement. The majority of Tavern’s functionality is exposed through a GraphQL API, which is used by both implants and the UI.
If you would like to help contribute to Tavern, please take a look at our open issues.
Deployment
This section will walk you through deploying a production ready instance of Tavern to GCP. If you’re just looking to play around with Tavern, feel free to run the docker image (kcarretto/tavern:latest) locally.
1. Create a GCP Project
Navigate to the GCP Console and create a new GCP project.
Make a note of the created Project ID as you’ll need that in a later step
2. Setup OAuth (Optional)
Note: These setup instructions assume you own a domain which you would like to host Tavern at.
If you want to configure OAuth for your Tavern Deployment, navigate to the GCP OAuth Consent Screen and create a new External consent flow. If you do not configure OAuth, Tavern will not perform any authentication or authorization for requests.
Provide details that users will see when logging into Tavern, for example:
- App Name: “Tavern”
- User Support Email: “[email protected]”
- App Logo: Upload something cool if you’d like, but then you’ll need to complete a verification process.
- App Domain: “https://tavern.mydomain.com/”
- Authorized Domains: “mydomain.com”
- Developer Contact Information: “[email protected]”
Add the “…/auth/userinfo.profile” scope, used by Tavern to obtain user names and photourls.
Next, add yourself as a “Test User”. Until you publish your app, only test users may complete the OAuth consent flow. If you didn’t select any options that require verification, you may publish your app now (so you won’t need to allowlist the users for your application).
Navigate to the Credentials Tool and select “Create Credentials” -> “OAuth client ID”. Be sure to add an “Authorized redirect URI” so that the consent flow redirects to the appropriate Tavern endpoint. For example “mydomain.com/oauth/authorize”. Save the resulting Client ID and Client secret for later.
Next, configure a CNAME record for the domain you’d like to host Tavern at (e.g. “tavern.mydomain.com”) to point to “ghs.googlehosted.com.”.
And that’s it! In the below sections on deployment, please ensure you properly configure your OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, and OAUTH_DOMAIN to ensure Tavern is properly configured.
3. Google Cloud CLI
Follow these instructions to install the gcloud CLI. This will enable you to quickly obtain credentials that terraform will use to authenticate. Alternatively, you may create a service account (with appropriate permissions) and obtain Application Default Credentials for it. See these Authentication Instructions for more information on how to configure GCP authentication for Terraform.
After installing the gcloud CLI, run gcloud auth application-default login
to obtain Application Default Credentials.
4. Terraform
- Follow these instructions to install the Terraform CLI.
- Clone the repo and navigate to the
terraform
directory. - Run
terraform init
to install the Google provider for terraform. - Run
terraform apply -var="gcp_project=<PROJECT_ID>" -var="oauth_client_id=<OAUTH_CLIENT_ID>" -var="oauth_client_secret=<OAUTH_CLIENT_SECRET>" -var="oauth_domain=<OAUTH_DOMAIN>"
to deploy Tavern!
Example:
terraform apply -var="gcp_project=new-realm-deployment" -var="oauth_client_id=12345.apps.googleusercontent.com" -var="oauth_client_secret=ABCDEFG" -var="oauth_domain=test-tavern.redteam.toys"
After terraform completes successfully, head to the DNS mappings for Cloud Run and wait for a certificate to successfully provision. This may take a while, so go enjoy a nice cup of coffee ☕
After your certificate has successfully provisioned, it may still take a while (e.g. an hour or two) before you are able to visit Tavern using your custom OAuth Domain (if configured).
CLI Variables
Name | Required | Description |
---|---|---|
gcp_project | Yes | Project ID of the GCP Project created in step 1. |
gcp_region | No | Region to deploy to. |
mysql_user | No | The MySQL user to create and connect with. |
mysql_passwd | No | The MySQL password to set and connect with. Autogenerated by default. |
mysql_dbname | No | MySQL Database to create and connect to. |
mysql_tier | No | The type of instance to run the Cloud SQL Database on. |
oauth_client_id | Only if OAuth is configured | The OAuth ClientID Tavern will use to connect to the IDP (Google). |
oauth_client_secret | Only if OAuth is configured | The OAuth Client Secret Tavern will use to connect to the IDP (Google). |
oauth_domain | Only if OAuth is configured | The OAuth Domain that the IDP should redirect to e.g. tavern.mydomain.com (should be the domain you set a CNAME record for while configuring OAuth). |
min_scale | No | The minimum number of containers to run, if set to 0 you may see cold boot latency. |
max_scale | No | The maximum number of containers to run. |
Manual Deployment Tips
Below are some deployment gotchas and notes that we try to address with Terraform, but can be a bit tricky if trying to deploy Tavern manually.
- MySQL version 8.0 must be started with the flag
default_authentication_plugin=caching_sha2_password
for authentication to work properly. A new user must be created for authentication. - When running in CloudRun, it’s best to connect to CloudSQL via a unix socket (so ensure the
MYSQL_NET
env var is set to “unix” ).- After adding a CloudSQL connection to your CloudRun instance, this unix socket is available at
/cloudsql/<CONNECTION_STRING>
(e.g./cloudsql/realm-379301:us-east4:tavern-db
).
- After adding a CloudSQL connection to your CloudRun instance, this unix socket is available at
- You must create a new database in your CloudSQL instance before launching Tavern and ensure the
MYSQL_DB
env var is set accordingly.
Configuration
Metrics
By default, Tavern does not export metrics. You may use the below environment configuration variables to enable Prometheus metric collection. These metrics become available at the “/metrics” endpoint configured. These metrics are hosted on a separate HTTP server such that it can be restricted to localhost (default). This is because the endpoint is unauthenticated, and would leak sensitive information if it was accessible.
Env Var | Description | Default | Required |
---|---|---|---|
ENABLE_METRICS | Set to any value to enable the “/metrics” endpoint. | Disabled | No |
HTTP_METRICS_LISTEN_ADDR | Listen address for the metrics HTTP server, it must be different than the value of HTTP_LISTEN_ADDR . |
127.0.0.1:8080 |
No |
MySQL
By default, Tavern operates an in-memory SQLite database. To persist data, a MySQL backend is supported. In order to configure Tavern to use MySQL, the MYSQL_ADDR
environment variable must be set to the host[:port]
of the database (e.g. 127.0.0.1
, mydb.com
, or mydb.com:3306
). You can reference the mysql.Config for additional information about Tavern’s MySQL configuration.
The following environment variables are currently supported for additional MySQL Configuration:
Env Var | Description | Default | Required |
---|---|---|---|
MYSQL_ADDR | Address of the MySQL server (e.g. host[:port] ) |
N/A | Yes |
MYSQL_NET | Network type (e.g. unix) | tcp | No |
MYSQL_USER | User to authenticate with | root | No |
MYSQL_PASSWD | Password to authenticate with | no password | No |
MYSQL_DB | Name of the database to use | tavern | No |
MYSQL_MAX_IDLE_CONNS | Integer value of max idle mysql connections to keep open | 10 | No |
MYSQL_MAX_OPEN_CONNS | Integer value of max mysql connections to open | 100 | No |
MYSQL_MAX_CONN_LIFETIME | Integer value of max mysql connection lifetime (in seconds) | 3600 | No |
Here is an example of running Tavern locally with a MySQL backend:
MYSQL_USER="admin" MYSQL_ADDR="127.0.0.1:3306" go run ./tavern
When no value is set for MYSQL_ADDR
, the default SQLite backend is used:
MYSQL_USER="admin" go run ./tavern/
2022/03/08 05:46:06 no value found for environment var 'MYSQL_ADDR', starting tavern with SQLite
OAuth
By default, user authentication is disabled for Tavern. This means that anyone can login and be granted a session. To restrict who is able to access your deployment, Tavern supports OAuth configuration (using Google OAuth).
To obtain a client_id and a client_secret for Google OAuth, please follow their instructions to create an application.
The following environment variables are required for OAuth Configuration:
Env Var | Description |
---|---|
OAUTH_CLIENT_ID | The OAuth client_id Tavern will use to communicate with an identity provider (Google) |
OAUTH_CLIENT_SECRET | The OAuth client_secret Tavern will use to authenticate to an identity provider (Google) |
OAUTH_DOMAIN | The domain Tavern is being hosted at, that the identity provider (Google) should redirect users to after completing the consent flow |
Here is an example of running Tavern locally with OAuth configured:
OAUTH_CLIENT_ID=123 OAUTH_CLIENT_SECRET=456 OAUTH_DOMAIN=127.0.0.1 go run ./tavern
2022/03/09 05:32:58 no value found for environment var 'MYSQL_ADDR', starting tavern with SQLite
2022/03/09 05:32:58 listening on 0.0.0.0:80
When no OAuth configuration is provided, authentication is disabled:
go run ./tavern
2022/03/09 05:24:43 no value found for environment var 'MYSQL_ADDR', starting tavern with SQLite
2022/03/09 05:24:43 WARNING: OAuth is not configured, authentication disabled
When partial OAuth configuration is provided, Tavern will error. This is to protect against inadvertently starting Tavern with authentication disabled.
OAUTH_CLIENT_ID=123 go run ./tavern
2022/03/09 05:31:46 no value found for environment var 'MYSQL_ADDR', starting tavern with SQLite
2022/03/09 05:31:46 [FATAL] To configure OAuth, must provide value for environment var 'OAUTH_CLIENT_SECRET'
exit status 1
How it Works
Tavern hosts two endpoints to support OAuth:
- A login handler (
/oauth/login
) which redirects users to Google’s OAuth consent flow- This endpoint sets a JWT cookie for the user, such that the OAuth state parameter can safely be verified later to prevent against CSRF attacks
- Currently the keys used to sign and verify JWTs are generated at server start, meaning if the server is restarted while a user is in the middle of an OAuth flow, it will fail and the user will need to restart the flow
- An authorization handler (
/oauth/authorize
) which users are redirected to by Google after completing Google’s OAuth consent flow- This handler is responsible for obtaining a user’s profile information from Google using an OAuth access token, and creates the user’s account if it does not exist yet
Trust on First Use
Tavern supports a Trust on First Use (TOFU) authentication model, meaning the first user to successfully authenticate will be granted admin permissions. Subsequent users that login will have accounts created, but will require activation before they can interact with any Tavern APIs. Only admin users may activate other users.
CLI Application Authentication
Tavern supports access_tokens
in place of a session cookie for authentication, intended for use by CLI applications that require authenticated access to the Tavern API. Currently, we only support Golang clients via the provided realm.pub/tavern/cli/auth
package. This package relies on the user’s existing browser session (requiring OAuth) to relay authentication credentials (an access_token
) to the application. Your CLI package may wish to cache these credentials securely to avoid unnecessary reauthentication. Here is an example of using this package:
package main
import (
"context"
"fmt"
"net/http"
"time"
"github.com/pkg/browser"
"realm.pub/tavern/cli/auth"
)
func main() {
// Set Timeout (includes time for user login via browser)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
// Setup your Tavern URL (e.g. from env vars)
tavernURL := "http://127.0.0.1"
// Configure Browser (uses the default system browser)
browser := auth.BrowserFunc(browser.OpenURL)
// Open Browser and Obtain Access Token (via 127.0.0.1 redirect)
token, err := auth.Authenticate(ctx, browser, tavernURL)
if err != nil {
panic(err)
}
// Example Tavern HTTP Request
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/status", tavernURL), nil)
if err != nil {
panic(err)
}
// Authenticate Request
token.Authenticate(req)
// Send the request etc...
}
If you require authenticated access to the Tavern API outside of Golang, you may implement something similar to what this package does under the hood. THIS IS NOT SUPPORTED. We highly recommend relying on the provided Golang package instead, as our Authentication API may change and cause outages for your application.
Test Data
Running Tavern with the ENABLE_TEST_DATA
environment variable set will populate the database with test data. This is useful for UI development, testing, or just interacting with Tavern and seeing how it works.
ENABLE_TEST_DATA=1 go run ./tavern
2023/02/24 01:02:37 [WARN] MySQL is not configured, using SQLite
2023/02/24 01:02:37 [WARN] OAuth is not configured, authentication disabled
2023/02/24 01:02:37 [WARN] Test data is enabled
2023/02/24 01:02:37 Starting HTTP server on 0.0.0.0:80
Default Tomes
Running Tavern with the DISABLE_DEFAULT_TOMES
environment variable set will disable uploading the default tomes. This is useful if they are unnecessary, or if you have a custom fork of them available somewhere for import.
DISABLE_DEFAULT_TOMES=1 go run ./tavern
2024/03/02 01:32:22 [WARN] No value for 'HTTP_LISTEN_ADDR' provided, defaulting to 0.0.0.0:80
2024/03/02 01:32:22 [WARN] MySQL is not configured, using SQLite
2024/03/02 01:32:22 [WARN] OAuth is not configured, authentication disabled
2024/03/02 01:32:22 [WARN] No value for 'DB_MAX_IDLE_CONNS' provided, defaulting to 10
2024/03/02 01:32:22 [WARN] No value for 'DB_MAX_OPEN_CONNS' provided, defaulting to 100
2024/03/02 01:32:22 [WARN] No value for 'DB_MAX_CONN_LIFETIME' provided, defaulting to 3600
2024/03/02 01:32:22 Starting HTTP server on 0.0.0.0:80
PPROF
Running Tavern with the ENABLE_PPROF
environment variable set will enable performance profiling information to be collected and accessible. This should never be set for a production deployment as it will be unauthenticated and may provide access to sensitive information, it is intended for development purposes only. Read more on how to use pprof
with tavern in the Developer Guide.
Build and publish tavern container
If you want to deploy tavern without using the published version you’ll have to build and publish your own container.
Build your container
cd ./realm
docker build --tag tavern:dev --file ./docker/tavern.Dockerfile .
Publish your container to docker hub
If you haven’t before sign-up for a docker hub account and login with the CLI docker login
docker tag tavern:dev <YOUR_DOCKER_HUB_USERNAME>/tavern:dev
docker push <YOUR_DOCKER_HUB_USERNAME>/tavern:dev
Specify your container during terraform deploy
terraform apply -var="gcp_project=<PROJECT_ID>" -var="oauth_client_id=<OAUTH_CLIENT_ID>" -var="oauth_client_secret=<OAUTH_CLIENT_SECRET>" -var="oauth_domain=<OAUTH_DOMAIN>" -var="tavern_container_image=<YOUR_DOCKER_HUB_USERNAME>/tavern:dev"
GraphQL API
Playground
If you’d like to explore the Graph API and try out some queries, head to the /graphiql
endpoint of your Tavern deployment. This endpoint exposes an interactive playground for you to experiment with GraphQL queries. Currently, this is used to fill gaps between developed backend functionality and in-development frontend functionality.
Within the GraphIQL Playground, you’ll be able to access documentation on the various queries and mutations available to you. For example, the updateUser
mutation is useful for activating new users and granting admin privileges to others.
Activate User Example
Activate the user with the ID 47244640258
.
mutation ActivateUser {
updateUser(userID: 47244640258, input:{isActivated:true}) {
id
name
isAdmin
isActivated
}
}
Activate Admin Example
Activate the user with the ID 47244640258
and grant them admin privileges.
mutation ActivateAdmin {
updateUser(userID: 47244640258, input:{isAdmin:true, isActivated:true}) {
id
name
isAdmin
isActivated
}
}
List Tomes Example
List information about all available tomes.
query Tomes {
tomes {
name
tactic
supportModel
files {
id
name
size
}
}
}
List Tomes (Filter) Example
List information about all available tomes which are made for persistence.
query PeristenceTomes {
tomes(where:{tactic: PERSISTENCE}) {
name
tactic
supportModel
files {
id
name
size
}
}
}
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.